package com.songlanyun.common.kqUtils;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import org.springframework.stereotype.Component;


@Component
public class Sign {
	
	private static final Log LOGGER = LogFactory.getLog(Sign.class);

	
	private static final String ENCRYP_TYPE = "SHA256WithRSA";

	private static final String CHARSET = "UTF-8";

	private String publicCertPath = "/work/shop/test/CFCA_sandbox.cer";
	private String pfxPrivateCertPath = "/work/shop/test/20190805.3300000002975573.pfx";
	private String privateCertPath = "/work/shop/test/20190805.3300000002975573.pem";

	private PrivateKey privateKey;

	private PublicKey publicKey;

	public void init(QuickPayProperties quickPayProperties){
		publicCertPath = quickPayProperties.getPublicCertPath();
		pfxPrivateCertPath = quickPayProperties.getPfxPrivateCertPath();
		privateCertPath = quickPayProperties.getPrivateCertPath();
	}

	public void createPrivateKey() {
		java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		try {
			//mock提供pfx和pem两种私钥证书格式读取方式，建议使用pfx格式证书：
			//pem: getPrivateKeyStreamFromPEM()
			//pfx: getPrivateKeyStreamFromPFX()
			byte[] b = Base64.decodeBase64(getPrivateKeyStreamFromPFX().getBytes(CHARSET));
			// 生成私匙
			KeyFactory kf = KeyFactory.getInstance("RSA");
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(b);
			PrivateKey privateKey = kf.generatePrivate(keySpec);
			this.privateKey = privateKey;
			LOGGER.info(String.format(" [XPAY-SDK] init private key success!!! private key path : [ %s ] !! ",
					this.privateCertPath));
		} catch (NoSuchAlgorithmException e) {
			LOGGER.error(" [XPAY-SDK]  createPrivateKey() NoSuchAlgorithmException : ", e);
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			LOGGER.error(" [XPAY-SDK]  createPrivateKey() InvalidKeySpecException : ", e);
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 从pfx格式证书读取私钥
	 * @return
	 */
	private String getPrivateKeyStreamFromPFX() {
		try {
			KeyStore ks = KeyStore.getInstance("PKCS12");
			FileInputStream ksfis = new FileInputStream(pfxPrivateCertPath);
			char[] keyPwd = "123456".toCharArray();
			ks.load(ksfis, keyPwd);
			PrivateKey priK = (PrivateKey) ks.getKey(ks.aliases().nextElement(), keyPwd);
			return Base64.encodeBase64String(priK.getEncoded());

		} catch (FileNotFoundException e) {
			LOGGER.error(" [XPAY-SDK]  getPrivateKeyStreamfromPFX() FileNotFoundException : ", e);
			e.printStackTrace();
		} catch (IOException e) {
			LOGGER.error(" [XPAY-SDK]  getPrivateKeyStreamfromPFX() IOException : ", e);
			e.printStackTrace();
		} catch (KeyStoreException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (CertificateException e) {
			e.printStackTrace();
		} catch (UnrecoverableKeyException e) {
			e.printStackTrace();
		} finally {
			
		}
		return null;
	}
	
	/**
	 * 读取pem格式私钥
	 * @return
	 */
	private String getPrivateKeyStreamFromPEM() {
		BufferedReader br = null;
		StringBuffer key = new StringBuffer();
		try {
			br = new BufferedReader(new FileReader(privateCertPath));
			String s = br.readLine();
			s = br.readLine();
			while (!StringUtils.isEmpty(s) && s.charAt(0) != '-') {
				key.append(s + "\r");
				s = br.readLine();
			}
		} catch (FileNotFoundException e) {
			LOGGER.error(" [XPAY-SDK]  getPrivateKeyStream() FileNotFoundException : ", e);
			e.printStackTrace();
		} catch (IOException e) {
			LOGGER.error(" [XPAY-SDK]  getPrivateKeyStream() IOException : ", e);
			e.printStackTrace();
		} finally {
			try {
				if (br != null) {
					br.close();
				}
			} catch (IOException e) {
				LOGGER.error(" [XPAY-SDK]  getPrivateKeyStream() close IOException : ", e);
				e.printStackTrace();
			}
		}
		return key.toString();
	}
	public void createPublicKey() {
		FileInputStream input = null;
		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			input = new FileInputStream(publicCertPath);
			X509Certificate cer = (X509Certificate) cf.generateCertificate(input);

			PublicKey pk = cer.getPublicKey();
			this.publicKey = pk;
			LOGGER.info(String.format(" [XPAY-SDK] init public key success!!! public key path : [ %s ] !! ",
					this.publicCertPath));
		} catch (CertificateException e) {
			LOGGER.error(" [XPAY-SDK]  createPublicKey() CertificateException : ", e);
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			LOGGER.error(" [XPAY-SDK]  createPublicKey() FileNotFoundException : ", e);
			e.printStackTrace();
		} finally {
			try {
				if (input != null) {
					input.close();
				}
			} catch (IOException e) {
				LOGGER.error(" [XPAY-SDK]  createPublicKey() IOException : ", e);
				e.printStackTrace();
			}
		}
	}
	public String doSign(Map<String,Object> request)
	{
		String retStr = "";
		String signField = getSignField(request);
		try {
			Signature signature = Signature.getInstance(ENCRYP_TYPE);
			signature.initSign(this.privateKey);
			signature.update(signField.getBytes(CHARSET));
			byte[] sign = signature.sign();
			retStr = new String(Base64.encodeBase64(sign), CHARSET);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return retStr;
	}
	public void verificate(String sign,Map<String,Object> response)
	{
		boolean pass = false;
		try {
			//获取sign明文字段
			String signField = getRespSignField(response);
			System.out.println("signField==="+signField);
			Signature sig = Signature.getInstance("SHA256withRSA");
			sig.initVerify(this.publicKey);
			byte[] signFields = Base64.decodeBase64(sign.getBytes(CHARSET));
			sig.update(signField.getBytes(CHARSET));
			pass = sig.verify(signFields);
			System.out.println("verificate is pass:"+pass);
			System.out.println(response.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	public boolean verificateNotify(String sign,Map<String,Object> response)
	{
		boolean pass = false;
		try {
			//获取sign明文字段
			String signField = getNotifySignField(response);
			System.out.println("signField==="+signField);
			Signature sig = Signature.getInstance("SHA256withRSA");
			sig.initVerify(this.publicKey);
			byte[] signFields = Base64.decodeBase64(sign.getBytes(CHARSET));
			sig.update(signField.getBytes(CHARSET));
			pass = sig.verify(signFields);

			System.out.println("verificate is pass:"+pass);
			System.out.println(response.toString());
			return pass;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return pass;
	}
	
	public static void main(String[] args) {
		//签名
		Sign sign = new Sign();
		try{
			String signData = "sign";
			Signature signature = Signature.getInstance(ENCRYP_TYPE);
			sign.createPrivateKey();
			signature.initSign(sign.privateKey);
			signature.update(signData.getBytes(CHARSET));
			byte[] sign_byte = signature.sign();
			//验证
			Signature sig = Signature.getInstance(ENCRYP_TYPE);
			sign.createPublicKey();
			sig.initVerify(sign.publicKey);
			sig.update(signData.getBytes(CHARSET));
			boolean pass = sig.verify(sign_byte);
			System.out.println(pass);
		}catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
	
	private String getSignField(Map<String,Object> request) {

		Map<String, Object> requestMap = new TreeMap<String, Object>();
		
		requestMap.put("memberCode", request.get("memberCode"));
		requestMap.put("merchantId", request.get("merchantId"));
		requestMap.put("txnType", request.get("txnType"));
		if (StringUtils.isNotEmpty(request.get("terminalId"))) {
			requestMap.put("terminalId", request.get("terminalId"));
		}
		if (StringUtils.isNotEmpty(request.get("amt"))) {
			requestMap.put("amt", request.get("amt"));
		}
		if (StringUtils.isNotEmpty(request.get("externalTraceNo"))) {
			requestMap.put("externalTraceNo", request.get("externalTraceNo"));
		}
		if (StringUtils.isNotEmpty(request.get("requestTime"))) {
			requestMap.put("requestTime", request.get("requestTime"));
		}
		if (StringUtils.isNotEmpty(request.get("stlDate"))) {
			requestMap.put("stlDate", request.get("stlDate"));
		}
		if (StringUtils.isNotEmpty(request.get("clientIp"))) {
			requestMap.put("clientIp", request.get("clientIp"));
		}
		if (StringUtils.isNotEmpty(request.get("idBizCtrl"))) {
			requestMap.put("idBizCtrl", request.get("idBizCtrl"));
		}
		if (StringUtils.isNotEmpty(request.get("origIdBizCtrl"))) {
			requestMap.put("origIdBizCtrl", request.get("origIdBizCtrl"));
		}
		if (StringUtils.isNotEmpty(request.get("origIdBiz"))) {
			requestMap.put("origIdBiz",request.get("origIdBiz"));
		}
		if (StringUtils.isNotEmpty(request.get("origTxnType"))){
			requestMap.put("origTxnType", request.get("origTxnType"));
		}
		StringBuilder sb = new StringBuilder();
		int i = 0;
		for (String key : requestMap.keySet()) {
			sb.append(key);
			sb.append("=");
			sb.append(requestMap.get(key));
			if (i < requestMap.size() - 1) {
				sb.append("&");
			}
			i++;
		}
		 String s =StringUtils.removeAllBlank(sb.toString());
		 System.out.println("明文串："+s);
		 return s;
	}
	
	private String getRespSignField(Map<String,Object> request) {
		Map<String, String> requestMap = new TreeMap<String, String>();
		requestMap.put("merchantId", request.get("merchantId").toString());
		requestMap.put("txnType", request.get("txnType").toString());
		if (StringUtils.isNotEmpty(request.get("terminalId"))) {
			requestMap.put("terminalId", request.get("terminalId").toString());
		}
		if (StringUtils.isNotEmpty(request.get("requestTime"))) {
			requestMap.put("requestTime", request.get("requestTime").toString());
		}
		if (StringUtils.isNotEmpty(request.get("externalTraceNo"))) {
			requestMap.put("externalTraceNo", request.get("externalTraceNo").toString());
		}
		if (StringUtils.isNotEmpty(request.get("amt"))) {
			requestMap.put("amt", request.get("amt").toString());
		}
		if (StringUtils.isNotEmpty(request.get("resultCode"))) {
			requestMap.put("resultCode", request.get("resultCode").toString());
		}
		if (StringUtils.isNotEmpty(request.get("idBizCtrl"))) {
			requestMap.put("idBizCtrl", request.get("idBizCtrl").toString());
		}
		if (StringUtils.isNotEmpty(request.get("txnFlg"))) {
			requestMap.put("txnFlg", request.get("txnFlg").toString());
		}
		if (StringUtils.isNotEmpty(request.get("stlDate"))) {
			requestMap.put("stlDate", request.get("stlDate").toString());
		}
		if (request.get("mpayInfo") != null) {
			
			Map<String,Object> m = new TreeMap<String, Object>();
			m = JSON.parseObject(request.get("mpayInfo").toString(), Map.class,Feature.OrderedField);
			System.out.println("mpayInfo=="+request.get("mpayInfo"));
			requestMap.put("mpayInfo", m.toString());
		}
		if (StringUtils.isNotEmpty(request.get("txnTime"))) {
			requestMap.put("txnTime", request.get("txnTime").toString());
		}
		if (StringUtils.isNotEmpty(request.get("refundAmt"))) {
			requestMap.put("refundAmt", request.get("refundAmt").toString());
		}
		if (StringUtils.isNotEmpty(request.get("idBiz"))) {
			requestMap.put("idBiz", request.get("idBiz").toString());
		}
		if (StringUtils.isNotEmpty(request.get("origIdBiz"))) {
			requestMap.put("origIdBiz", request.get("origIdBiz").toString());
		}
		if (StringUtils.isNotEmpty(request.get("origTxnType"))){
			requestMap.put("origTxnType", request.get("origTxnType").toString());
		}

		//拼接加签字段
		StringBuilder sb = new StringBuilder();
		int i = 0;
		for (String key : requestMap.keySet()) {
			sb.append(key);
			sb.append("=");
			sb.append(requestMap.get(key));
			if (i < requestMap.size() - 1) {
				sb.append("&");
			}
			i++;
		}

		return StringUtils.removeAllBlank(sb.toString());
	}
	
	
	
	private String getNotifySignField(Map<String,Object> request) {
		Map<String, String> requestMap = new TreeMap<String, String>();
		requestMap.put("merchantId", request.get("merchantId").toString());
		requestMap.put("txnType", request.get("txnType").toString());
		requestMap.put("outOrderNo", request.get("outOrderNo").toString());
		requestMap.put("innerOrderNo", request.get("innerOrderNo").toString());
		requestMap.put("txnFlg", request.get("txnFlg").toString());
		requestMap.put("amt", request.get("amt").toString());
		requestMap.put("appType", request.get("appType").toString());
		requestMap.put("txnTime", request.get("txnTime").toString());
		requestMap.put("idBiz", request.get("idBiz").toString());
		//拼接加签字段
		StringBuilder sb = new StringBuilder();
		int i = 0;
		for (String key : requestMap.keySet()) {
			sb.append(key);
			sb.append("=");
			sb.append(requestMap.get(key));
			if (i < requestMap.size() - 1) {
				sb.append("&");
			}
			i++;
		}

		return StringUtils.removeAllBlank(sb.toString());
	}
	
	
}
